-
Notifications
You must be signed in to change notification settings - Fork 30
Attachment package refactor #311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
packages/powersync_attachments_stream/lib/src/local_storage.dart
Outdated
Show resolved
Hide resolved
packages/powersync_attachments_stream/lib/src/local_storage.dart
Outdated
Show resolved
Hide resolved
packages/powersync_attachments_stream/lib/src/attachment_service.dart
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more comments from a quick look.
Also, I assume supabase-todolist-new-attachments was copied from the old one? For a review it would be easier if the changes were in the same project so I can see what has changed. But that can happen in the end.
packages/powersync_attachments_stream/lib/powersync_attachments_stream.dart
Outdated
Show resolved
Hide resolved
packages/powersync_attachments_stream/lib/powersync_attachments_stream.dart
Outdated
Show resolved
Hide resolved
packages/powersync_attachments_stream/lib/src/attachment_queue_service.dart
Outdated
Show resolved
Hide resolved
packages/powersync_attachments_stream/lib/src/attachment_queue_service.dart
Outdated
Show resolved
Hide resolved
packages/powersync_attachments_stream/test/local_storage_test.dart
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more comments, most of them pretty minor again :)
packages/powersync_attachments_stream/lib/src/abstractions/local_storage.dart
Outdated
Show resolved
Hide resolved
packages/powersync_attachments_stream/lib/src/attachment_queue_service.dart
Outdated
Show resolved
Hide resolved
packages/powersync_attachments_stream/lib/src/implementations/attachment_context.dart
Outdated
Show resolved
Hide resolved
packages/powersync_attachments_stream/lib/src/implementations/attachment_service.dart
Outdated
Show resolved
Hide resolved
import 'dart:async'; | ||
import 'dart:io'; | ||
import 'dart:typed_data'; | ||
import 'package:path/path.dart' as p; | ||
import '../abstractions/local_storage.dart'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Typically one inserts an empty line between dart:
and package:
imports and then another one before local imports. All the effective Dart samples do that, but it's not spelled out explicitly. I've added a suggestion for this here, it affects other files as well.
import 'dart:async'; | |
import 'dart:io'; | |
import 'dart:typed_data'; | |
import 'package:path/path.dart' as p; | |
import '../abstractions/local_storage.dart'; | |
import 'dart:async'; | |
import 'dart:io'; | |
import 'dart:typed_data'; | |
import 'package:path/path.dart' as p; | |
import '../abstractions/local_storage.dart'; |
attachment.copyWith(state: AttachmentState.archived), | ||
); | ||
} | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need this break
anymore in recent Dart versions.
packages/powersync_attachments_stream/lib/src/storage/io_local_storage.dart
Outdated
Show resolved
Hide resolved
handleData: (data, sink) { | ||
sink.add(data); | ||
// Simple throttle implementation - just delay the next event | ||
Future.delayed(throttle); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This future is unawaited and will not actually throttle anything.
I think it would be easier to not throttle the stream here and instead add a duration parameter to watchActiveAttachments
. We can forward that to db.watch()
instead of duplicating the throttle implementation here. That means we wouldn't be throttling manualTriggers
anymore, but I think that's fine.
packages/powersync_attachments_stream/lib/src/attachment_queue_service.dart
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation looks good now, I have a few nits on types. I also think we should strive to not introduce classes / interfaces we don't strictly need, especially if they're publicly exported. Also more generally:
- Please run
dart format
on the package. I also recommend enabling format on save. - I think a few more tests with an actual database would be nice to have, you can take a look at the Kotlin tests for attachments for inspiration. We can test on native platforms only in the beginning, you can probably copy something like this to set up databases. But it's still a bit tricky, let me know if you need help with this. I can also push a basic scaffold for tests to this branch.
@@ -0,0 +1,10 @@ | |||
# This file tracks properties of this Flutter project. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this package is no longer a Flutter project, I think we can delete this file.
/// Default exports for native platforms (dart:io). For web, use 'common.dart'. | ||
export 'common.dart'; | ||
export 'src/storage/io_local_storage.dart'; | ||
export 'src/attachment_queue_service.dart'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is exported by common.dart
now and can be removed here.
/// | ||
/// [id]: The ID of the attachment to delete. | ||
/// [tx]: The database context to use for the operation. | ||
Future<void> deleteAttachment(String id, dynamic context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be dynamic, can this be a SqliteWriteContext
?
/// | ||
/// [id]: The ID of the attachment to delete. | ||
/// [tx]: The database context to use for the operation. | ||
Future<void> deleteAttachment(String id, dynamic context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also this function isn't called at all, I assume that should happen in SyncingService.deleteAttachment
?
/// [attachment]: The attachment to upsert. | ||
/// [context]: The database transaction/context to use for the operation. | ||
/// Returns the upserted [Attachment]. | ||
Future<Attachment> upsertAttachment(Attachment attachment, dynamic context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
context
should likely be a SqliteWriteContext
here as well?
@@ -0,0 +1,34 @@ | |||
import 'dart:typed_data'; | |||
|
|||
abstract class AbstractLocalStorageAdapter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make this an interface class and just name it LocalStorageAdapter
:
abstract class AbstractLocalStorageAdapter { | |
abstract interface class LocalStorageAdapter { |
return fileExtension != null | ||
? '$attachmentId.$fileExtension' | ||
: '$attachmentId.dat'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return fileExtension != null | |
? '$attachmentId.$fileExtension' | |
: '$attachmentId.dat'; | |
return '$attachmentId.${fileExtension ?? 'dat'}'; |
.cast<Attachment?>() | ||
.firstWhere( | ||
(a) => a != null && a.id == item.id, | ||
orElse: () => null, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a more intuitive way to express this without the cast()
may be
.cast<Attachment?>() | |
.firstWhere( | |
(a) => a != null && a.id == item.id, | |
orElse: () => null, | |
); | |
.where((a) => a.id == item.id) | |
.firstOrNull; |
required String mediaType, | ||
String? fileExtension, | ||
String? metaData, | ||
required Future<void> Function(dynamic context, Attachment attachment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
required Future<void> Function(dynamic context, Attachment attachment) | |
required Future<void> Function(SqliteWriteContext context, Attachment attachment) |
/// The default implementation assumes the attachment record already exists locally. | ||
Future<Attachment> deleteFile({ | ||
required String attachmentId, | ||
required Future<void> Function(dynamic context, Attachment attachment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
required Future<void> Function(dynamic context, Attachment attachment) | |
required Future<void> Function(SqliteWriteContext context, Attachment attachment) |
await attachmentQueue.deleteFile( | ||
attachmentId: todo.photoId!, | ||
updateHook: (context, attachment) async { | ||
// await context.execute("UPDATE todos SET photo_id = NULL WHERE id = ?", [todo.id]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should probably still do the un-assignment here, is uncommenting this perhaps a TODO?
…handling in powersync_attachments_stream package and added unit tests
…/powersync.dart into attachment-package-refactor
…_stream package. Removed unused imports, improved error handling during file uploads and downloads, and enhanced and renamed logging for better traceability of attachment operations.
…hods, improving clarity on attachment lifecycle management, state handling, and local storage operations.
…hments package implementation to supabase-todolist demo.
… path for local storage in the powersync_attachments_stream package. Simplified the attachment watching logic. Updated README for clarity on schema and attachment states.
… classes to utilize the new interface and adjusted README for consistency.
… package. Updated attachment queue initialization to use localStorage, and enhanced README for clarity on storage handling. Added tests for edge cases and robustness in local storage operations.
…consistency. Removed outdated comment regarding attachments directory in attachment queue service documentation.
…dated photo capture widget to save attachments as byte data instead of streams, and modified related methods in the attachment queue service to accept byte data. Updated tests to reflect changes in data handling.
…comments, replacing multiple error handler parameters with a single optional error handler for sync-related errors.
…fixed various formatting
fa2f16e
to
248d748
Compare
No description provided.